#include "common.h"
#include "burst.h"
#include "nemu.h"
uint32_t dram_read(hwaddr_t, size_t);
void dram_write(hwaddr_t, size_t, uint32_t);

/* Memory accessing interfaces */

uint32_t hwaddr_read(hwaddr_t addr, size_t len) {
	int i1 = read_cache_L1(addr);
	uint32_t set1 = (addr >> cache_L1_b) & (cache_L1_S - 1);//单独把set写一下
	uint32_t bias = addr & (cache_L1_B - 1);
	uint8_t ret[BURST_LEN * 2];
	if (bias + len > cache_L1_B) {
    	int i2 = read_cache_L1(addr + cache_L1_B - bias);
		uint32_t set2 = ((addr + cache_L1_B - bias) >> cache_L1_b) & (cache_L1_S - 1);//单独把set写一下，可能改变
    	memcpy(ret, cache_L1[set1][i1].data + bias, cache_L1_B - bias);
    	memcpy(ret  + cache_L1_B - bias, cache_L1[set2][i2].data, len - (cache_L1_B - bias));
		} 
	else {
    	memcpy(ret, cache_L1[set1][i1].data + bias, len);
	}
	uint32_t tmp = 0;
	uint32_t result = unalign_rw(ret + tmp, 4) & (~0u >> ((4 - len) << 3));
	return result;
}
// uint32_t hwaddr_read(hwaddr_t addr, size_t len) {
// 	return dram_read(addr, len) & (~0u >> ((4 - len) << 3));
// }
void hwaddr_write(hwaddr_t addr, size_t len, uint32_t data) {
	write_cache_L1(addr, len, data);
}
// void hwaddr_write(hwaddr_t addr, size_t len, uint32_t data) {
// 	dram_write(addr, len, data);
// }
hwaddr_t page_translate(lnaddr_t addr){
	if((cpu.cr0.protect_enable & 0x1) == 0 || (cpu.cr0.paging & 0x1) == 0){
		return addr;
	}
	hwaddr_t page_directory_base = (cpu.cr3.page_directory_base) << 12;
	hwaddr_t page_directory_entry_addr = page_directory_base + ((addr >> 22) & 0x3ff) * 4;
	uint32_t page_directory_entry = hwaddr_read(page_directory_entry_addr, 4);
	if((page_directory_entry & 0x1) == 0){
		panic("page directory entry not present");
		assert(0);
	}
	hwaddr_t page_table_base = (page_directory_entry & 0xfffff000);
	hwaddr_t page_table_entry_addr = page_table_base + ((addr >> 12) & 0x3ff) *4;
	uint32_t page_table_entry = hwaddr_read(page_table_entry_addr, 4);
	if((page_table_entry & 0x1) == 0){
		panic("page table entry not present");
		assert(0);
	}
	hwaddr_t frame_address = (page_table_entry & 0xfffff000);
	hwaddr_t physical_address = frame_address + (addr & 0xfff);
	return physical_address;
}
uint32_t lnaddr_read(lnaddr_t addr, size_t len) {
	assert(len == 1 || len == 2 || len == 4);
	if(0){
		/* this is a special case, you can handle it later*/
		assert(0);
	} else{
		hwaddr_t hwaddr = page_translate(addr);
		return hwaddr_read(hwaddr, len);
	}
}
// uint32_t lnaddr_read(lnaddr_t addr, size_t len) {
// 	return hwaddr_read(addr, len);
// }

void lnaddr_write(lnaddr_t addr, size_t len, uint32_t data) {
	assert(len == 1 || len == 2 || len == 4);
	if(0){
		/* this is a special case, you can handle it later*/
		assert(0);
	} else{
		hwaddr_t hwaddr = page_translate(addr);
		hwaddr_write(hwaddr, len, data);
	}
}
// void lnaddr_write(lnaddr_t addr, size_t len, uint32_t data) {
// 	hwaddr_write(addr, len, data);
// }
lnaddr_t seg_translate(swaddr_t addr, size_t len, uint8_t sreg){
	if((cpu.cr0.protect_enable & 0x1) == 0){
		return addr;
	}
	return cpu.sreg[sreg].base + addr;
}

uint32_t swaddr_read(swaddr_t addr, size_t len, uint8_t sreg) {
	assert(len == 1 || len == 2 || len == 4);
	lnaddr_t lnaddr = seg_translate(addr, len, sreg);
	return lnaddr_read(lnaddr, len);
}

// uint32_t swaddr_read(swaddr_t addr, size_t len) {
// #ifdef DEBUG
// 	assert(len == 1 || len == 2 || len == 4);
// #endif
// 	return lnaddr_read(addr, len);
// }

void swaddr_write(swaddr_t addr, size_t len, uint32_t data, uint8_t sreg) {
	assert(len == 1 || len == 2 || len == 4);
	lnaddr_t lnaddr = seg_translate(addr, len, sreg);
	lnaddr_write(lnaddr, len, data);
}

// void swaddr_write(swaddr_t addr, size_t len, uint32_t data) {
// #ifdef DEBUG
// 	assert(len == 1 || len == 2 || len == 4);
// #endif
// 	lnaddr_write(addr, len, data);
// }

